package cliente.parsing;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import cliente.domain.Status;
import cliente.domain.User;

import java.io.IOException;


/**
 * Class that contains common code to parse XML responses and generate the matching domain objects
 */
public class XmlParsingUtils {

    /**
     * Extract a User from a XML document node/element.
     *
     * @param xpp XmlPullParser
     *
     * @return User instance with all parsed properties.
     *
     * @throws XmlPullParserException Thrown on parsing exception
     * @throws IOException Thrown on I/O Exception
     */
    public static User parseUser(XmlPullParser xpp) throws XmlPullParserException, IOException {
        User user = new User();

        // if it's not a <user> node, return immediately
        if (!"user".equals(xpp.getName())) {
            return user;
        }

        // else advance element cursor and start looking for known user sub-elements
        xpp.next();
        int eventType = xpp.getEventType();

        do {
            switch (eventType) {
                // search for new tags and match them to the User object properties
                case XmlPullParser.START_TAG:
                    // this if-elseif chain is far from being optimised, but it's simpler to understand
                    if ("id".equals(xpp.getName())) {
                        // these are simple XML elements with no attributes, only text
                        user.setId(getElementText(xpp));
                    } else if ("name".equals(xpp.getName())) {
                        user.setName(getElementText(xpp));
                    } else if ("screen_name".equals(xpp.getName())) {
                        user.setScreenName(getElementText(xpp));
                    } else if ("description".equals(xpp.getName())) {
                        user.setDescription(getElementText(xpp));
                    } else if ("url".equals(xpp.getName())) {
                        user.setUrl(getElementText(xpp));
                    } else if ("protected".equals(xpp.getName())) {
                        user.setProtectedProfile(Boolean.parseBoolean(getElementText(xpp)));
                    } else if ("followers_count".equals(xpp.getName())) {
                        user.setFollowers(Integer.parseInt(getElementText(xpp)));
                    } else if ("friends_count".equals(xpp.getName())) {
                        user.setFollowing(Integer.parseInt(getElementText(xpp)));
                    } else if ("created_at".equals(xpp.getName())) {
                        user.setCreatedAt(getElementText(xpp));
                    } else if ("statuses_count".equals(xpp.getName())) {
                        user.setTweets(Integer.parseInt(getElementText(xpp)));
                    } else if ("status".equals(xpp.getName())) {
                        // this one is different, it's not a value, it's another element!
                        // in this case, it's a tweet, so call parse status to return a Status object from this
                        // XML element.
                        user.setLastTweet(parseStatus(xpp));
                    } else {
                        // ignore all else - comment to avoid verbose output
                        System.out.println("XmlParsingUtils.parseUser(): ignored attribute: '" + xpp.getName() + "'");
                    }
            }
            // advance to next event (at current level)
            eventType = xpp.next();
        } while (!((eventType == XmlPullParser.END_TAG) && ("user".equals(xpp.getName()))));
        // while condition above loops until </user> is found

        return user;
    }


    /**
     * Extract a Status from a XML document node/element.
     *
     * @param xpp XmlPullParser
     *
     * @return Status instance with all parsed properties.
     *
     * @throws XmlPullParserException Thrown on parsing exception
     * @throws IOException Thrown on I/O Exception
     */
    public static Status parseStatus(XmlPullParser xpp) throws XmlPullParserException, IOException {
        Status status = new Status();

        // if it's not a <status> node, return immediately
        if (!"status".equals(xpp.getName())) {
            return status;
        }

        // else advance element cursor and start looking for known status sub-elements
        xpp.next();
        int eventType = xpp.getEventType();
        do {
            switch (eventType) {
                case XmlPullParser.START_TAG:
                    if ("id".equals(xpp.getName())) {
                        status.setId(getElementText(xpp));
                    } else if ("created_at".equals(xpp.getName())) {
                        status.setCreatedAt(getElementText(xpp));
                    } else if ("text".equals(xpp.getName())) {
                        status.setText(getElementText(xpp));
                    } else if ("source".equals(xpp.getName())) {
                        status.setSource(getElementText(xpp));
                    } else if ("truncated".equals(xpp.getName())) {
                        status.setTruncated(Boolean.parseBoolean(getElementText(xpp)));
                    } else if ("in_reply_to_status_id".equals(xpp.getName())) {
                        status.setReplyToTweetId(getElementText(xpp));
                    } else if ("in_reply_to_user_id".equals(xpp.getName())) {
                        status.setReplyToUserId(getElementText(xpp));
                    } else {
                        // ignore all else - comment to avoid verbose output
                        System.out.println("XmlParsingUtils.parseStatus(): ignored attribute '" + xpp.getName() + "'");
                    }
            }
            // advance to next event (at current level)
            eventType = xpp.next();
        } while (!((eventType == XmlPullParser.END_TAG) && ("status".equals(xpp.getName()))));
        // while condition above loops until </status> is found

        return status;
    }

    /**
     * Extract the TEXT of the current XML element/node.
     *
     * @param xpp XmlPullParser current parser.
     *
     * @return Text.
     * @throws java.io.IOException Thrown in I/O Exceptions
     * @throws org.xmlpull.v1.XmlPullParserException Thrown on parsing exceptions
     */
    public static String getElementText(XmlPullParser xpp) throws XmlPullParserException, IOException {
        int eventType = xpp.getEventType();
        do {
            switch (eventType) {
                case XmlPullParser.TEXT:
                    return xpp.getText().trim();
            }
            eventType = xpp.next();
        } while (eventType != XmlPullParser.END_TAG);

        // return nothing if text not found for element
        return null;
    }

    private XmlParsingUtils() {
    }
}
